//! 发送器

use crate::datagram::DatagramHeader;
use crate::{Addr, ETP};
use core::time::Duration;
use futures_intrusive::channel::shared::*;
use futures_intrusive::sync::GenericMutex;
use futures_util::{
    future::{select, Either},
    pin_mut,
};
use spin::Lazy;
cfg_if::cfg_if! {
    if #[cfg(feature = "role_center")] {
        use alloc::collections::BTreeMap;
    } else{
        use crate::consts::RETRY_DATA_LEN;
        use fixed_queue::LinearMap;
        type BTreeMap<K, V> = LinearMap<K, V, RETRY_DATA_LEN>;
    }
}
type Mutex<T> = GenericMutex<spin::Mutex<()>, T>;

/// 等待远程设备回复
type WaitRepSender = GenericOneshotSender<spin::Mutex<()>, ()>;
struct WaitRepFuture {
    recver: GenericOneshotReceiver<spin::Mutex<()>, ()>,
}
impl WaitRepFuture {
    pub fn new() -> (WaitRepFuture, WaitRepSender) {
        let (sender, recver) = generic_oneshot_channel();
        let future = WaitRepFuture { recver };
        (future, sender)
    }
    pub async fn wait(self, timeout: Duration) -> Result<u64, ()> {
        let start_time = task_stream::now();
        let timeout_fut = task_stream::sleep(timeout);
        let waitrep_fut = self.recver.receive();
        pin_mut!(timeout_fut);
        pin_mut!(waitrep_fut);
        match select(waitrep_fut, timeout_fut).await {
            Either::Left((_, _)) => {
                let consum = task_stream::now() - start_time;
                Ok(consum)
            }
            Either::Right(_) => Err(()),
        }
    }
}

static WAITLIST: Lazy<Mutex<BTreeMap<u16, WaitRepSender>>> =
    Lazy::new(|| Mutex::new(BTreeMap::new(), false));

/// 发送数据到指定设备，失败时重试retry次，返回用时时间
pub(crate) async fn send(
    port: u8,
    dst: &Addr,
    body: &[u8],
    retry: u8,
    mut timeout: u64,
) -> Result<u64, ()> {
    let mut head = DatagramHeader::new(port, dst.port(), None);
    head.set_control(if retry == 0 { 2 } else { 3 });
    let buf = head.generate(body);
    let index = head.index();

    for _i in 0..retry + 1 {
        let (future, sender) = WaitRepFuture::new();
        WAITLIST.lock().await.insert(index, sender);
        let etp = ETP.get().unwrap();
        let _err = etp.send(&buf, dst.conn()).await;
        if let Ok(consum) = future.wait(Duration::from_millis(timeout)).await {
            return Ok(consum);
        } else {
            timeout *= 2;
        }
    }
    return Err(());
}

/// 收到响应数据
pub(crate) async fn finish(index: u16) {
    if let Some(sender) = WAITLIST.lock().await.remove(&index) {
        let _err = sender.send(());
    }
}
